{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.1、从感知机到神经网络"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.1、神经网络的例子"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "用图来表示神经网络的话，如图3-1所示。  \n",
    "我们把最左边的一列称为`输入层`，最右边的一列称为`输出层`，中间的一列称为`中间层`。  \n",
    "`中间层`有时也称为`隐藏层`。  \n",
    "“隐藏”一词的意思是，隐藏层的神经元（和输入层、输出层不同）肉眼看不见。  \n",
    "另外，本书中把输入层到输出层依次称为第0层、第1层、第2层（层号之所以从0开始，是为了方便后面基于Python进行实现）。  \n",
    "图3-1中，第0层对应输入层，第1层对应中间层，第2层对应输出层。  \n",
    "<img src=\"./imgs/3_1.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图 3-1 中的网络一共由 3 层神经元构成，但实质上只有2层神经元有权重，因此将其称为“2层网络”。  \n",
    "请注意，有的书也会根据构成网络的层数，把图3-1的网络称为“3层网络”。  \n",
    "本书将根据实质上拥有权重的层数（输入层、隐藏层、输出层的总数减去1后的数量）来表示网络的名称。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.2、复习感知机"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_2.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图3-2中的感知机接收x1和x2两个输入信号，输出y。如果用数学式来\n",
    "表示图3-2中的感知机，则如式（3.1）所示:$$\n",
    "y = \\left\\{ \\begin{array} { l l } { 0 } & { \\left( b + w _ { 1 } x _ { 1 } + w _ { 2 } x _ { 2 } \\leqslant 0 \\right) } \\\\ { 1 } & { \\left( b + w _ { 1 } x _ { 1 } + w _ { 2 } x _ { 2 } > 0 \\right) } \\end{array} \\right.\n",
    "$$<center>式（3.1）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "b是被称为偏置的参数，用于控制神经元被激活的容易程度；  \n",
    "而w1和w2是表示各个信号的权重的参数，用于控制各个信号的重要性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在图3-2的网络中，偏置b并没有被画出来。如果要明确地表示出b，可以像图3-3那样做。  \n",
    "图3-3中添加了权重为b的输入信号1。这个感知机将x1、 x2、 1三个信号作为神经元的输入，将其和各自的权重相乘后，传送至下一个神经元。  \n",
    "在下一个神经元中，计算这些加权信号的总和。  \n",
    "如果这个总和超过0，则输出1，否则输出0。  \n",
    "另外，由于偏置的输入信号一直是1，所以为了区别于其他神经元，我们在图中把这个神经元整个涂成灰色。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在将式（3.1）改写成更加简洁的形式。为了简化式（3.1），我们用一个\n",
    "函数来表示这种分情况的动作（超过0则输出1，否则输出0）。引入新函数\n",
    "h(x)，将式（3.1）改写成下面的式（3.2）和式（3.3）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "y = h \\left( b + w _ { 1 } x _ { 1 } + w _ { 2 } x _ { 2 } \\right)\n",
    "$$<center>式（3.2）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "h ( x ) = \\left\\{ \\begin{array} { l l } { 0 } & { ( x \\leqslant 0 ) } \\\\ { 1 } & { ( x > 0 ) } \\end{array} \\right.\n",
    "$$<center>式（3.3）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_3.jpg\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.3、激活函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "h（x）函数会将输入信号的总和转换为输出信号，这种函数一般称为`激活函数`（activation function）。  \n",
    "激活函数的作用在于`决定如何来激活输入信号的总和`。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "式（3.2）分两个阶段进行处理，先计算输入信号的加权总和，然后用激活函数转换这一总和。  \n",
    "因此，如果将式（3.2）写得详细一点，则可以分成下面两个式子：$$\n",
    "a = b + w _ { 1 } x _ { 1 } + w _ { 2 } x _ { 2 }\n",
    "$$ <center>式（3.4）</center>  \n",
    "$$\n",
    "y = h ( a )\n",
    "$$<center>式（3.5）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "式（3.4）计算加权输入信号和偏置的总和，记为a。  \n",
    "然后，式（3.5）用h()函数将a转换为输出y。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前的神经元都是用一个○表示的，如果要在图中明确表示出式（3.4）\n",
    "和式（3.5），则可以像图3-4这样做：<img src=\"./imgs/3_4.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "表示神经元的○中明确显示了激活函数的计算过程，即信号的加权总和为节点a，然后节点a被激活函数h()转换成节点y。  \n",
    "本书中，“神经元”和“节点”两个术语的含义相同。  \n",
    "这里，我们称a和y为“节点”，其实它和之前所说的“神经元”含义相同"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通常如图3-5的左图所示，神经元用一个○表示。本书中，在可以明确\n",
    "神经网络的动作的情况下，将在图中明确显示激活函数的计算过程，如图3-5\n",
    "的右图所示。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_5.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`激活函数是连接感知机和神经网络的桥梁。 `"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本书在使用“感知机”一词时，没有严格统一它所指的算法。  \n",
    "一般而言，“朴素感知机”是指单层网络，指的是激活函数使用了阶跃函数的模型。  \n",
    "“多层感知机”是指神经网络，即使用sigmoid 函数（ 后述）等平滑的激活函数的多层网络。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.2、激活函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.1、sigmoid函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "神经网络中经常使用的一个激活函数就是式（3.6）表示的`sigmoid函数`（sigmoid function）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "h ( x ) = \\frac { 1 } { 1 + \\exp ( - x ) }\n",
    "$$<center>式（3.6）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "式（3.6）中的exp(−x)表示e的-x次方，e是纳皮尔常数2.7182...。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "神经网络中用sigmoid函数作为激活函数，进行信号的转换，转换后的信号被传送给下一个神经元。  \n",
    "实际上，上一章介绍的感知机和接下来要介绍的神经网络的主要区别就在于这个激活函数。  \n",
    "其他方面，比如神经元的多层连接的构造、信号的传递方法等，基本上和感知机是一样的。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.2、阶跃函数的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`阶跃函数`是指一旦输入超过阈值，就切换输出的函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "def step_function(x):\n",
    "    if x > 0:\n",
    "        return 1\n",
    "    else:\n",
    "        return 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个实现简单、易于理解，但是参数 x只能接受实数（浮点数）。  \n",
    "也就是说，允许形如step_function(3.0)的调用，但不允许参数取NumPy数组，例如step_function(np.array([1.0, 2.0]))。  \n",
    "为了便于后面的操作，我们把它修改为支持NumPy数组的实现。为此，可以考虑下述实现。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "def step_function(x):\n",
    "    y = x > 0\n",
    "    return y.astype(np.int)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于使用了NumPy中的“技巧”，可能会有点难理解。  \n",
    "我们通过Python解释器的例子来看一下这里用了什么技巧。  \n",
    "下面这个例子中准备了NumPy数组x，并对这个NumPy数组进行了不等号运算。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-1.,  1.,  2.])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x=np.array([-1.0,1.0,2.0])\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False,  True,  True])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y=x>0\n",
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对NumPy数组进行不等号运算后，数组的各个元素都会进行不等号运算，生成一个布尔型数组。  \n",
    "这里，数组 x中大于0的元素被转换为 True，小于等于0的元素被转换为False，从而生成一个新的数组y。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数组y是一个布尔型数组，但是我们想要的阶跃函数是会输出int型的0或1的函数。  \n",
    "因此，需要把数组y的元素类型从布尔型转换为int型。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 1])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = y.astype(np.int)\n",
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以用 astype()方法转换NumPy数组的类型。   \n",
    "astype()方法通过参数指定期望的类型，这个例子中是 np.int型。   \n",
    "Python中将布尔型转换为int型后， True会转换为1， False会转换为0。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.3、阶跃函数的图形"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x11b0cf358>]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(-0.1, 1.1)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pylab as plt\n",
    "def step_function(x):\n",
    "    return np.array(x > 0, dtype=np.int)\n",
    "x = np.arange(-5.0, 5.0, 0.1)\n",
    "y = step_function(x)\n",
    "plt.plot(x, y)\n",
    "plt.ylim(-0.1, 1.1) # 指定y轴的范围\n",
    "plt.show()\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "阶跃函数以0为界，输出从0切换为1（或者从1切换为0）。  \n",
    "它的值呈阶梯式变化，所以称为`阶跃函数`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.4、sigmoid函数的实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sigmoid(x):\n",
    "    return 1 / (1 + np.exp(-x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.26894142, 0.73105858, 0.88079708])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x=np.array([-1.0,1.0,2.0])\n",
    "sigmoid(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "sigmoid函数的实现能支持NumPy数组，秘密就在于NumPy的广播功能。  \n",
    "根据NumPy 的广播功能，如果在标量和NumPy数组之间进行运算，则标量会和NumPy数组的各个元素进行运算。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2., 3., 4.])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "array([1.        , 0.5       , 0.33333333])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t=np.array([1.0,2.0,3.0])\n",
    "1.0+t\n",
    "1.0/t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x11b124710>]"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(-0.1, 1.1)"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = np.arange(-5.0, 5.0, 0.1)\n",
    "y = sigmoid(x)\n",
    "plt.plot(x, y)\n",
    "plt.ylim(-0.1, 1.1) # 指定y轴的范围\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.5、sigmoid函数和阶跃函数的比较"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不同点：  \n",
    "1、`平滑性`的不同。`sigmoid函数`是一条`平滑的曲线`，输出随着输入发生`连续性的变化`。而`阶跃函数`以0为界，输出`发生急剧性的变化`。  \n",
    "sigmoid函数的平滑性对神经网络的学习具有重要意义。  \n",
    "2、相对于阶跃函数只能返回0或1， sigmoid函数可以返回0.731 . . .、 0.880 . . .等实数（这一点和刚才的平滑性有关）。\n",
    "也就是说，感知机中神经元之间流动的是0或1的二元信号，而神经网络中流动的是连续的实数值信号。  \n",
    "共同点：  \n",
    "1、输入信号为重要信息时，阶跃函数和sigmoid函数都会输出较大的值；当输入信号为不重要的信息时，两者都输出较小的值。  \n",
    "2、不管输入信号有多小，或者有多大，输出信号的值都在0到1之间。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.6、非线性函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "阶跃函数和sigmoid函数还有其他共同点，就是两者均为非线性函数。  \n",
    "sigmoid函数是一条曲线，阶跃函数是一条像阶梯一样的折线，两者都属于非线性的函数。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在介绍激活函数时，经常会看到“非线性函数”和“线性函数”等术语。  \n",
    "函数本来是输入某个值后会返回一个值的转换器。  \n",
    "向这个转换器输入某个值后，输出值是输入值的常数倍的函数称为线性函数（用数学式表示为h(x) = cx。 c为常数）。  \n",
    "因此，线性函数是一条笔直的直线。  \n",
    "而非线性函数，顾名思义，指的是不像线性函数那样呈现出一条直线的函数。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**神经网络的激活函数必须使用非线性函数。**  \n",
    "因为使用线性函数的话，加深神经网络的层数就没有意义了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线性函数的问题在于，不管如何加深层数，总是存在与之等效的“无隐藏层的神经网络”。  \n",
    "为了具体地（稍微直观地）理解这一点，我们来思考下面这个简单的例子。  \n",
    "这里我们考虑把线性函数 h(x) = cx 作为激活函数，把y(x) = h(h(h(x)))的运算对应3层神经网络 A。  这个运算会进行y(x) = c × c × c × x的乘法运算，但是同样的处理可以由y(x) = ax（注意，a = c3）这一次乘法运算（即没有隐藏层的神经网络）来表示。  \n",
    "如本例所示，使用线性函数时，无法发挥多层网络带来的优势。  \n",
    "因此，为了发挥叠加层所带来的优势，激活函数必须使用非线性函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.7、ReLU函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在神经网络发展的历史上， sigmoid函数很早就开始被使用了，而最近则主要\n",
    "使用**ReLU**（Rectified Linear Unit）函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ReLU函数在输入大于0时，直接输出该值；在输入小于等于0时，输\n",
    "出0（图3-9）\n",
    "<img src=\"./imgs/3_9.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ReLU函数可以表示为:$$\n",
    "h ( x ) = \\left\\{ \\begin{array} { l l } { x } & { ( x > 0 ) } \\\\ { 0 } & { ( x \\leqslant 0 ) } \\end{array} \\right.\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def relu(x):\n",
    "    return np.maximum(0, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本章剩余部分的内容仍将使用sigmoid函数作为激活函数，但在本书的后半部分，则将主要使用ReLU函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.3、多维数组的运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3.1、多维数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3, 4])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(4,)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A=np.array([1,2,3,4])\n",
    "A\n",
    "np.ndim(A)\n",
    "A.shape\n",
    "A.shape[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2],\n",
       "       [3, 4],\n",
       "       [5, 6]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(3, 2)"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B=np.array([[1,2],[3,4],[5,6]])\n",
    "B\n",
    "np.ndim(B)\n",
    "B.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二维数组也称为**矩阵**（matrix）。如图3-10所示，数组的横向排列称为**行**（row），纵向排列称为**列**（column）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3.2、矩阵乘法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "来介绍矩阵（二维数组）的乘积。比如2 × 2的矩阵，其乘积可以像图3-11这样进行计算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_10_3_11.png\"></img>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 2)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "array([[19, 22],\n",
       "       [43, 50]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.array([[1,2], [3,4]])\n",
    "B=np.array([[5,6],[7,8]])\n",
    "B.shape\n",
    "np.dot(A,B) #矩阵乘法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "矩阵A的第1维的元素个数（3）必须和矩阵B的第0维的元素个数（3）相等才能相乘。  \n",
    "2 × 3的矩阵和3 × 2 的矩阵的乘积"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[22, 28],\n",
       "       [49, 64]])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.array([[1,2,3], [4,5,6]])\n",
    "B = np.array([[1,2], [3,4], [5,6]])\n",
    "np.dot(A, B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 2)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "ename": "ValueError",
     "evalue": "shapes (2,3) and (2,2) not aligned: 3 (dim 1) != 2 (dim 0)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-29-4ef5e019f4e6>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mC\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mC\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m: shapes (2,3) and (2,2) not aligned: 3 (dim 1) != 2 (dim 0)"
     ]
    }
   ],
   "source": [
    "C = np.array([[1,2], [3,4]])\n",
    "C.shape\n",
    "A.shape\n",
    "np.dot(A,C) #无法相乘"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_12.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当A是二维矩阵、 B是一维数组时，如图3-13所示，对应维度的元素个数要保持一致的原则依然成立\n",
    "<img src=\"./imgs/3_13.png\"></img>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3, 2)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "array([23, 53, 83])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.array([[1,2], [3, 4], [5,6]])\n",
    "A.shape\n",
    "B=np.array([7,8])\n",
    "B.shape\n",
    "np.dot(A,B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3.3、神经网络的内积"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用NumPy矩阵来实现神经网络。这里我们以图3-14中的简单神经网络为对象。  \n",
    "这个神经网络省略了偏置和激活函数，只有权重。  \n",
    "<img src=\"./imgs/3_14.png\"></img>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "array([[1, 3, 5],\n",
       "       [2, 4, 6]])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = np.array([1, 2])\n",
    "X.shape\n",
    "W = np.array([[1, 3, 5], [2, 4, 6]])\n",
    "W\n",
    "W.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 5, 11, 17])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y = np.dot(X, W)\n",
    "Y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.4、3层神经网络的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里我们以图3-15的3层神经网络为对象，实现从输入到输出的（前向）处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_15.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.1、符号确认"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在介绍神经网络中的处理之前，我们先导入$$\n",
    "w _ { 12 } ^ { ( 1 ) } , a _ { 1 } ^ { ( 1 ) }\n",
    "$$等符号。  \n",
    "这些符号可能看上去有些复杂，不过因为只在本节使用，稍微读一下就跳过去也问题不大。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本节的重点是神经网络的运算可以作为矩阵运算打包进行。  \n",
    "因为神经网络各层的运算是通过矩阵的乘法运算打包进行的（从宏观视角来考虑），所以即便忘了（未记忆）具体的符号规则，也不影响理解后面的内容。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们先从定义符号开始。  \n",
    "请看图3-16。图3-16中只突出显示了从输入层神经元x2到后一层的神经元的权重。  \n",
    "权重和隐藏层的神经元的右上角有一个“(1)”，它表示权重和神经元的层号（即第1层的权重、第1层的神经元）。  \n",
    "此外，权重的右下角有两个数字，它们是后一层的神经元和前一层的神经元的索引号。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_16.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.2、各层间信号传递的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在看一下从输入层到第1层的第1个神经元的信号传递过程，如图3-17所示。\n",
    "<img src=\"./imgs/3_17.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图3-17中增加了表示偏置的神经元“1”。  \n",
    "请注意，偏置的右下角的索引号只有一个。这是因为前一层的偏置神经元（神经元“1”）只有一个。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了确认前面的内容，现在用数学式表示$$\n",
    "a _ { 1 } ^ { ( 1 ) }\n",
    "$$ 。 $$\n",
    "a _ { 1 } ^ { ( 1 ) }\n",
    "$$通过加权信号和偏\n",
    "置的和按如下方式进行计算：$$\n",
    "a _ { 1 } ^ { ( 1 ) } = w _ { 11 } ^ { ( 1 ) } x _ { 1 } + w _ { 12 } ^ { ( 1 ) } x _ { 2 } + b _ { 1 } ^ { ( 1 ) }\n",
    "$$\n",
    "<center>式（3.8）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果使用矩阵的乘法运算，则可以将第1层的加权和表示成下面\n",
    "的式（3.9）。$$\n",
    "\\boldsymbol { A } ^ { ( 1 ) } = \\boldsymbol { X } \\boldsymbol { W } ^ { ( 1 ) } + \\boldsymbol { B } ^ { ( 1 ) }\n",
    "$$<center>式（3.9）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其中， A(1)、 X、 B (1)、 W (1)如下所示:\n",
    "$$\n",
    "\\boldsymbol { A } ^ { ( 1 ) } = \\left( \\begin{array} { l l l } { a _ { 1 } ^ { ( 1 ) } } & { a _ { 2 } ^ { ( 1 ) } } & { a _ { 3 } ^ { ( 1 ) } } \\end{array} \\right)\n",
    "$$\n",
    "$$\n",
    "X = \\left( \\begin{array} { l l } { x _ { 1 } } & { x _ { 2 } } \\end{array} \\right)\n",
    "$$\n",
    "$$\n",
    "\\boldsymbol { B } ^ { ( 1 ) } = \\left( \\begin{array} { l l l } { b _ { 1 } ^ { ( 1 ) } } & { b _ { 2 } ^ { ( 1 ) } } & { b _ { 3 } ^ { ( 1 ) } } \\end{array} \\right)\n",
    "$$\n",
    "$$\n",
    "\\boldsymbol { W } ^ { ( 1 ) } = \\left( \\begin{array} { c c c } { w _ { 11 } ^ { ( 1 ) } } & { w _ { 21 } ^ { ( 1 ) } } & { w _ { 31 } ^ { ( 1 ) } } \\\\ { w _ { 12 } ^ { ( 1 ) } } & { w _ { 22 } ^ { ( 1 ) } } & { w _ { 32 } ^ { ( 1 ) } } \\end{array} \\right)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面我们用NumPy多维数组来实现式（3.9），这里将输入信号、权重、偏置设置成任意值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(3,)"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = np.array([1.0, 0.5])\n",
    "W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])\n",
    "B1 = np.array([0.1, 0.2, 0.3])\n",
    "\n",
    "X.shape\n",
    "W1.shape\n",
    "B1.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.3, 0.7, 1.1])"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A1=np.dot(X,W1)+B1\n",
    "A1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们观察第1层中激活函数的计算过程。如果把这个计算过程用图来表示的话，则如图3-18所示:<img src=\"./imgs/3_18.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图3-18所示，隐藏层的加权和（加权信号和偏置的总和）用a表示，被激活函数转换后的信号用z表示。  \n",
    "此外，图中h()表示激活函数，这里我们使用的是sigmoid函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面，我们来实现第1层到第2层的信号传递（图3-19）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_19.png\"></img>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3,)"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(3, 2)"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Z1 = sigmoid(A1)\n",
    "W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])\n",
    "B2 = np.array([0.1, 0.2])\n",
    "\n",
    "Z1.shape\n",
    "W2.shape\n",
    "B2.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "A2=np.dot(Z1,W2)+B2\n",
    "Z2=sigmoid(A2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后是第2层到输出层的信号传递（图3-20）。  \n",
    "输出层的实现也和之前的实现基本相同。  \n",
    "不过，最后的激活函数和之前的隐藏层有所不同。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "def identity_function(x):\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "W3 = np.array([[0.1, 0.3], [0.2, 0.4]])\n",
    "B3 = np.array([0.1, 0.2])\n",
    "A3 = np.dot(Z2, W3) + B3\n",
    "Y = identity_function(A3) # 或者Y = A3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们定义了 identity_function()函数（也称为“恒等函数”），并将其作为输出层的激活函数。  \n",
    "恒等函数会将输入按原样输出，因此，这个例子中没有必要特意定义identity_function()。  \n",
    "这里这样实现只是为了和之前的流程保持统一。  \n",
    "另外，图3-20中，输出层的激活函数用σ()表示，不同于隐藏层的激活函数h()（σ读作sigma）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_20.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输出层所用的激活函数，要根据求解问题的性质决定。  \n",
    "一般地，**回归问题**可以使用**恒等函数**，**二元分类问题**可以使用**sigmoid函数**，**多元分类问题**可以使用**softmax函数**。  \n",
    "关于输出层的激活函数，我们将在下一节详细介绍  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.3、代码实现小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们按照神经网络的实现惯例，只把权重记为大写字母W1，其他的（偏置或中间结果等）都用小写字母表示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "def init_network():\n",
    "    network = {}\n",
    "    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])\n",
    "    network['b1'] = np.array([0.1, 0.2, 0.3])\n",
    "    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])\n",
    "    network['b2'] = np.array([0.1, 0.2])\n",
    "    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])\n",
    "    network['b3'] = np.array([0.1, 0.2])\n",
    "    return network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "def forward(network, x):\n",
    "    W1, W2, W3 = network['W1'], network['W2'], network['W3']\n",
    "    b1, b2, b3 = network['b1'], network['b2'], network['b3']\n",
    "    a1 = np.dot(x, W1) + b1\n",
    "    z1 = sigmoid(a1)\n",
    "    a2 = np.dot(z1, W2) + b2\n",
    "    z2 = sigmoid(a2)\n",
    "    a3 = np.dot(z2, W3) + b3\n",
    "    y = identity_function(a3)\n",
    "    return y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.31682708, 0.69627909])"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "network = init_network()\n",
    "x = np.array([1.0, 0.5])\n",
    "y = forward(network, x)\n",
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "init_network()函数会进行权重和偏置的初始化，并将它们保存在字典变量network中。  \n",
    "这个字典变量network中保存了每一层所需的参数（权重和偏置）。   \n",
    "forward()函数中则封装了将输入信号转换为输出信号的处理过程。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里出现了**forward**（前向）一词，它表示的是`从输入到输出方向的传递处理`。  \n",
    "后面在进行神经网络的训练时，我们将介绍后向（backward，从输出到输入方向）的处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "至此，神经网络的前向处理的实现就完成了。  \n",
    "通过巧妙地使用NumPy多维数组，我们高效地实现了神经网络。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.5、输出层的设计"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "神经网络可以用在分类问题和回归问题上，不过需要根据情况改变输出层的激活函数。  \n",
    "一般而言，回归问题用恒等函数，分类问题用softmax函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.5.1、恒等函数和softmax函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "恒等函数会将输入按原样输出，对于输入的信息，不加以任何改动地直\n",
    "接输出。  \n",
    "因此，在输出层使用恒等函数时，输入信号会原封不动地被输出。  \n",
    "另外，将恒等函数的处理过程用之前的神经网络图来表示的话，则如图3-21\n",
    "所示。  \n",
    "和前面介绍的隐藏层的激活函数一样，恒等函数进行的转换处理可以用一根箭头来表示。  \n",
    "<img src=\"./imgs/3_21.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "分类问题中使用的softmax函数可以用下面的式（3.10）表示:\n",
    "$$\n",
    "y _ { k } = \\frac { \\exp \\left( a _ { k } \\right) } { \\sum _ { i = 1 } ^ { n } \\exp \\left( a _ { i } \\right) }\n",
    "$$<center>式（3.10）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "exp(x)是表示ex的指数函数（e是纳皮尔常数2.7182 . . .）。  \n",
    "式（3.10）表示假设输出层共有n个神经元，计算第k个神经元的输出yk。  \n",
    "如式（3.10）所示，softmax函数的分子是输入信号ak的指数函数，分母是所有输入信号的指数\n",
    "函数的和。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "用图表示softmax函数的话，如图3-22所示。  \n",
    "图3-22中， softmax函数的输出通过箭头与所有的输入信号相连。  \n",
    "这是因为，从式（3.10）可以看出，输出层的各个神经元都受到所有输入信号的影响。 \n",
    "<img src=\"./imgs/3_22.png\"></img>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.34985881, 18.17414537, 54.59815003])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([0.3, 2.9, 4.0])\n",
    "exp_a = np.exp(a) # 指数函数\n",
    "exp_a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "74.1221542101633"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum_exp_a = np.sum(exp_a) # 指数函数的和\n",
    "sum_exp_a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.01821127, 0.24519181, 0.73659691])"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = exp_a / sum_exp_a\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "def softmax(a):\n",
    "    exp_a = np.exp(a)\n",
    "    sum_exp_a = np.sum(exp_a)\n",
    "    y = exp_a / sum_exp_a\n",
    "    return y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.5.2、实现softmax函数时的注意事项"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的softmax函数的实现虽然正确描述了式（3.10），但在计算机的运算上有一定的缺陷。  \n",
    "这个缺陷就是**溢出问题**。  \n",
    "softmax函数的实现中要进行指数函数的运算，但是此时指数函数的值很容易变得非常大。  \n",
    "比如， e10的值会超过20000， e100会变成一个后面有40多个0的超大值， e1000的结果会返回一个表示无穷大的inf。  \n",
    "如果在这些超大值之间进行除法运算，结果会出现“不确定”的情况。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算机处理“数”时，数值必须在4字节或8字节的有限数据宽度内。  \n",
    "这意味着数存在有效位数，也就是说，可以表示的数值范围是有限的。  \n",
    "因此，会出现超大值无法表示的问题。  \n",
    "这个问题称为**溢出**，在进行计算机的运算时必须（常常）注意。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "softmax函数的实现可以像式（3.11）这样进行改进:$$\n",
    "\\begin{aligned} y _ { k } = \\frac { \\exp \\left( a _ { k } \\right) } { \\sum _ { i = 1 } ^ { n } \\exp \\left( a _ { i } \\right) } & = \\frac { \\operatorname { Cexp } \\left( a _ { k } \\right) } { \\mathrm { C } \\sum _ { i = 1 } ^ { n } \\exp \\left( a _ { i } \\right) } \\\\ & = \\frac { \\exp \\left( a _ { k } + \\log C \\right) } { \\sum _ { i = 1 } ^ { n } \\exp \\left( a _ { i } + \\log \\mathrm { C } \\right) } \\\\ & = \\frac { \\exp \\left( a _ { k } + C ^ { \\prime } \\right) } { \\sum _ { i = 1 } ^ { n } \\exp \\left( a _ { i } + C ^ { \\prime } \\right) } \\end{aligned}\n",
    "$$<center>式（3.11）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先，式（3.11）在分子和分母上都乘上C这个任意的常数（因为同时对分母和分子乘以相同的常数，所以计算结果不变）。  \n",
    "然后，把这个C移动到指数函数（exp）中，记为log C。  \n",
    "最后，把log C替换为另一个符号C'。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "式（3.11）说明，在进行softmax的指数函数的运算时，加上（或者减去）某个常数并不会改变运算的结果。  \n",
    "这里的C'可以使用任何值，但是为了防止溢出，一般会使用输入信号中的最大值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "来看一个具体的例子:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/ipykernel_launcher.py:2: RuntimeWarning: overflow encountered in exp\n",
      "  \n",
      "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/ipykernel_launcher.py:2: RuntimeWarning: invalid value encountered in true_divide\n",
      "  \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([nan, nan, nan])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1010, 1000, 990])\n",
    "np.exp(a)/np.sum(np.exp(a)) # softmax函数的运算，没有被正确计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([  0, -10, -20])"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = np.max(a) # 1010\n",
    "a-c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([9.99954600e-01, 4.53978686e-05, 2.06106005e-09])"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.exp(a - c) / np.sum(np.exp(a - c))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如该例所示，通过减去输入信号中的最大值（上例中的 c），我们发现原\n",
    "本为 nan（not a number，不确定）的地方，现在被正确计算了。  \n",
    "综上，我们可以像下面这样实现softmax函数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "def softmax(a):\n",
    "    c = np.max(a)\n",
    "    exp_a = np.exp(a - c) # 溢出对策\n",
    "    sum_exp_a = np.sum(exp_a)\n",
    "    y = exp_a / sum_exp_a\n",
    "    return y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.5.3、softmax函数的特征"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用softmax()函数，可以按如下方式计算神经网络的输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.01821127, 0.24519181, 0.73659691])"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([0.3, 2.9, 4.0])\n",
    "y=softmax(a)\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "softmax函数的输出是0.0到1.0之间的实数。   \n",
    "并且， softmax函数的输出值的总和是1。  \n",
    "`输出总和为1是softmax函数的一个重要性质。`  \n",
    "正因为有了这个性质，我们才可以把softmax函数的输出解释为“概率”。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从概率的结果来看，可以说“因为第2个元素的概率最高，所以答案是第2个类别”。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过使用softmax函数，我们可以用概率的（统计的）方法处理问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "即便使用了softmax函数，各个元素之间的大小关系也不会改变。  \n",
    "这是因为指数函数（y = exp(x)）是`单调递增函数`。<br>\n",
    "实际上，上例中a的各元素的大小关系和y的各元素的大小关系并没有改变。  \n",
    "比如， a的最大值是第2个元素， y的最大值也仍是第2个元素。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一般而言，**神经网络只把输出值最大的神经元所对应的类别作为识别结果**。<br>\n",
    "并且，即便使用softmax函数，输出值最大的神经元的位置也不会变。  \n",
    "因此，`神经网络在进行分类时，输出层的softmax函数可以省略`。<br>\n",
    "在实际的问题中，由于指数函数的运算需要一定的计算机运算量，因此输出层的softmax函数一般会被省略。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "求解机器学习问题的步骤可以分为“学习”和“推理”两个阶段。  \n",
    "首先， 在学习阶段进行模型的学习，然后，在推理阶段，用学到的模型对未知的数据进行推理（分类）。  \n",
    "如前所述，推理阶段一般会省略输出层的 softmax 函数。  \n",
    "在输出层使用 softmax 函数是因为它和神经网络的学习有关系（详细内容请参考下一章）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.5.4、输出层的神经元数量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输出层的神经元数量需要根据待解决的问题来决定。  \n",
    "对于分类问题，输出层的神经元数量一般设定为类别的数量。  \n",
    "比如，对于某个输入图像，预测是图中的数字0到9中的哪一个的问题（10类别分类问题），可以像图3-23这样，将输出层的神经元设定为10个。\n",
    "<img src=\"./imgs/3_23.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个例子中，输出层的神经元从上往下依次对应数字0, 1, . . ., 9。  \n",
    "此外，图中输出层的神经元的值用不同的灰度表示。  \n",
    "神经元y2颜色最深，输出的值最大。  \n",
    "这表明这个神经网络预测的是y2对应的类别，也就是“2”。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.6、手写数字识别"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假设学习已经全部结束，我们使用学习到的参数，先实现神经网络的“推理处理”。  \n",
    "这个推理处理也称为神经网络的**前向传播**（forward propagation）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和求解机器学习问题的步骤（分成学习和推理两个阶段进行）一样，使用神经网络解决问题时，也需要首先使用训练数据（学习数据）进行权重参数的学习；  \n",
    "进行推理时，使用刚才学习到的参数，对输入数据进行分类。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.6.1、MNIST数据集"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里使用的数据集是MNIST手写数字图像集。   \n",
    "MNIST是机器学习领域最有名的数据集之一，被应用于从简单的实验到发表的论文研究等各种场合。  \n",
    "实际上，在阅读图像识别或机器学习的论文时， MNIST数据集经常作为实验用的数据出现。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MNIST数据集是由0到9的数字图像构成的（图3-24）。  \n",
    "训练图像有6万张，测试图像有1万张，这些图像可以用于学习和推理。   \n",
    "MNIST数据集的一般使用方法是，先用训练图像进行学习，再用学习到的模型度量能在多大程度上对测试图像进行正确的分类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/3_24.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MNIST的图像数据是28像素 × 28像素的灰度图像（1通道），各个像素的取值在0到255之间。  \n",
    "每个图像数据都相应地标有“7”“2”“1”等标签。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下载数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading train-images-idx3-ubyte.gz ... \n",
      "Done\n",
      "Downloading train-labels-idx1-ubyte.gz ... \n",
      "Done\n",
      "Downloading t10k-images-idx3-ubyte.gz ... \n",
      "Done\n",
      "Downloading t10k-labels-idx1-ubyte.gz ... \n",
      "Done\n",
      "Converting train-images-idx3-ubyte.gz to NumPy Array ...\n",
      "Done\n",
      "Converting train-labels-idx1-ubyte.gz to NumPy Array ...\n",
      "Done\n",
      "Converting t10k-images-idx3-ubyte.gz to NumPy Array ...\n",
      "Done\n",
      "Converting t10k-labels-idx1-ubyte.gz to NumPy Array ...\n",
      "Done\n",
      "Creating pickle file ...\n",
      "Done!\n",
      "(60000, 784)\n",
      "(60000,)\n",
      "(10000, 784)\n",
      "(10000,)\n"
     ]
    }
   ],
   "source": [
    "import sys, os\n",
    "sys.path.append(os.pardir) # 为了导入父目录中的文件而进行的设定\n",
    "from dataset.mnist import load_mnist\n",
    "# 第一次调用会花费几分钟 ……\n",
    "(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,\n",
    "normalize=False)\n",
    "# 输出各个数据的形状\n",
    "print(x_train.shape) # (60000, 784)\n",
    "print(t_train.shape) # (60000,)\n",
    "print(x_test.shape) # (10000, 784)\n",
    "print(t_test.shape) # (10000,)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "导入dataset/mnist.py中的 load_mnist函数。  \n",
    "最后，使用 load_mnist函数，读入MNIST数据集。  \n",
    "第一次调用load_mnist函数时，因为要下载MNIST数据集，所以需要接入网络。  \n",
    "第2次及以后的调用只需读入保存在本地的文件（pickle文件）即可，因此处理所需的时间非常短。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "load_mnist函数以“(训练图像 ,训练标签 )，(测试图像，测试标签 )”的形式返回读入的MNIST数据。  \n",
    "此外，还可以像 load_mnist(normalize=True,flatten=True, one_hot_label=False) 这 样，设 置 3 个 参 数。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "第 1 个 参 数normalize设置是否将输入图像正规化为0.0～1.0的值。  \n",
    "如果将该参数设置为 False，则输入图像的像素会保持原来的0～255。  \n",
    "第2个参数 flatten设置是否展开输入图像（变成一维数组）。  \n",
    "如果将该参数设置为 False，则输入图像为1 × 28 × 28的三维数组；若设置为True，则输入图像会保存为由784个元素构成的一维数组。  \n",
    "第3个参数one_hot_label设置是否将标签保存为onehot表示（one-hot representation）。   \n",
    "one-hot表示是仅正确解标签为1，其余皆为0的数组，就像[0,0,1,0,0,0,0,0,0,0]这样。  \n",
    "当one_hot_label为False时，只是像 7、 2这样简单保存正确解标签；当 one_hot_label为 True时，标签则保存为one-hot表示。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python有pickle这个便利的功能。  \n",
    "这个功能可以将程序运行中的对象保存为文件。  \n",
    "如果加载保存过的pickle文件，可以立刻复原之前程序运行中的对象。  \n",
    "用于读入MNIST数据集的 load_mnist()函数内部也使用了pickle功能（在第2次及以后读入时）。  \n",
    "利用 pickle 功能，可以高效地完成MNIST数据的准备工作。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们试着显示MNIST图像，同时也确认一下数据。  \n",
    "图像的显示使用PIL（Python Image Library）模块。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys, os\n",
    "sys.path.append(os.pardir)\n",
    "import numpy as np\n",
    "from dataset.mnist import load_mnist\n",
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "def img_show(img):\n",
    "    pil_img = Image.fromarray(np.uint8(img))\n",
    "    pil_img.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "(784,)\n",
      "(28, 28)\n"
     ]
    }
   ],
   "source": [
    "(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,normalize=False)\n",
    "img = x_train[0]\n",
    "label = t_train[0]\n",
    "print(label) # 5\n",
    "print(img.shape) # (784,)\n",
    "img = img.reshape(28, 28) # 把图像的形状变成原来的尺寸\n",
    "print(img.shape) # (28, 28)\n",
    "img_show(img)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里需要注意的是， flatten=True时读入的图像是以一列（一维） NumPy\n",
    "数组的形式保存的。  \n",
    "因此，显示图像时，需要把它变为原来的28像素 × 28像素的形状。  \n",
    "可以通过 reshape()方法的参数指定期望的形状，更改NumPy数组的形状。  \n",
    "此外，还需要把保存为NumPy数组的图像数据转换为PIL用的数据对象，这个转换处理由Image.fromarray()来完成。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.6.2、神经网络的推理处理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们对这个MNIST数据集实现神经网络的推理处理。  \n",
    "神经网络的输入层有784个神经元，输出层有10个神经元。  \n",
    "输入层的784这个数字来源于图像大小的28 × 28 = 784，输出层的10这个数字来源于10类别分类（数\n",
    "字0到9，共10类别）。  \n",
    "此外，这个神经网络有2个隐藏层，第1个隐藏层有50个神经元，第2个隐藏层有100个神经元。  \n",
    "这个50和100可以设置为任何值。下面我们先定义 get_data()、 init_network()、 predict()这3个函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pickle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_data():\n",
    "    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)\n",
    "    return x_test, t_test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "def init_network(): \n",
    "    with open(\"./files/sample_weight.pkl\", 'rb') as f:\n",
    "        network = pickle.load(f)\n",
    "    return network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict(network, x):\n",
    "    W1, W2, W3 = network['W1'], network['W2'], network['W3']\n",
    "    b1, b2, b3 = network['b1'], network['b2'], network['b3']\n",
    "    a1 = np.dot(x, W1) + b1\n",
    "    z1 = sigmoid(a1)\n",
    "    a2 = np.dot(z1, W2) + b2\n",
    "    z2 = sigmoid(a2)\n",
    "    a3 = np.dot(z2, W3) + b3\n",
    "    y = softmax(a3)\n",
    "    return y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "init_network()会读入保存在pickle文件 sample_weight.pkl中的学习到的权重参数。  \n",
    "这个文件中以字典变量的形式保存了权重和偏置参数。  \n",
    "剩余的2个函数，和前面介绍的代码实现基本相同，无需再解释。  \n",
    "现在，我们用这3个函数来实现神经网络的推理处理。  \n",
    "然后，评价它的**识别精度**（accuracy），即能在多大程度上正确分类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy:0.9352\n"
     ]
    }
   ],
   "source": [
    "x, t = get_data()\n",
    "network = init_network()\n",
    "accuracy_cnt = 0\n",
    "for i in range(len(x)):\n",
    "    y = predict(network, x[i])\n",
    "    p = np.argmax(y) # 获取概率最高的元素的索引\n",
    "    if p == t[i]:\n",
    "        accuracy_cnt += 1\n",
    "print(\"Accuracy:\" + str(float(accuracy_cnt) / len(x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先获得MNIST数据集，生成网络。  \n",
    "接着，用 for语句逐一取出保存在 x中的图像数据，用predict()函数进行分类。   \n",
    "predict()函数以NumPy数组的形式输出各个标签对应的概率。  \n",
    "比如输出[0.1, 0.3, 0.2, ..., 0.04]的数组，该数组表示“0”的概率为0.1，“1”的概率为0.3，等等。  然后，我们取出这个概率列表中的最大值的索引（第几个元素的概率最高），作为预测结\n",
    "果。  \n",
    "可以用 np.argmax(x)函数取出数组中的最大值的索引， np.argmax(x)将获取被赋给参数x的数组中的最大值元素的索引。  \n",
    "最后，比较神经网络所预测的答案和正确解标签，将回答正确的概率作为识别精度。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外，在这个例子中，我们把load_mnist函数的参数normalize设置成了True。  \n",
    "将 normalize设置成 True后，函数内部会进行转换，将图像的各个像素值除以255，使得数据的值在0.0～1.0的范围内。  \n",
    "像这样把数据限定到某个范围内的处理称为**正规化**（normalization）。  \n",
    "此外，对神经网络的输入数据进行某种既定的转换称为**预处理**（pre-processing）。  \n",
    "这里，作为对输入图像的一种预处理，我们进行了正规化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "预处理在神经网络（深度学习）中非常实用，其有效性已在提高识别性能和学习的效率等众多实验中得到证明。  \n",
    "在刚才的例子中，作为一种预处理，我们将各个像素值除以 255，进行了简单的正规化。  \n",
    "实际上，很多预处理都会考虑到数据的整体分布。  \n",
    "比如，利用数据整体的均值或标准差，移动数据，使数据整体以 0为中心分布，或者进行正规化，把数据的延展控制在一定范围内。  \n",
    "除此之外，还有将数据整体的分布形状均匀化的方法，即数据**白化**（whitening）等"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.6.3、批处理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们来关注输入数据和权重参数的“形状”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(10000, 784)"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x,_=get_data()\n",
    "network=init_network()\n",
    "W1,W2,W3=network['W1'],network['W2'],network['W3']\n",
    "x.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(784,)"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[0].shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(784, 50)"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(50, 100)"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(100, 10)"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "W1.shape\n",
    "W2.shape\n",
    "W3.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们通过上述结果来确认一下多维数组的对应维度的元素个数是否一致（省略了偏置）。  \n",
    "如图3-26所示:\n",
    "<img src=\"./imgs/3_26.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以发现，多维数组的对应维度的元素个数确实是一致的。  \n",
    "此外，我们还可以确认最终的结果是输出了元素个数为10 的一维数组。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从整体的处理流程来看，图3-26中，输入一个由784个元素（原本是一个28 × 28的二维数组）构成的一维数组后，输出一个有10个元素的一维数组。  \n",
    "这是只输入一张图像数据时的处理流程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们来考虑打包输入多张图像的情形。比如，我们想用 predict()函数一次性打包处理100张图像。  \n",
    "为此，可以把x的形状改为100 × 784，将100张图像打包作为输入数据。  \n",
    "用图表示的话，如图3-27所示：\n",
    "<img src=\"./imgs/3_27.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图 3-27 所示，输入数据的形状为 100 × 784，输出数据的形状为100 × 10。  \n",
    "这表示输入的100张图像的结果被一次性输出了。  \n",
    "比如， x[0]和y[0]中保存了第0张图像及其推理结果， x[1]和 y[1]中保存了第1张图像及\n",
    "其推理结果，等等。  \n",
    "这种打包式的输入数据称为**批**（batch）。  \n",
    "批有“捆”的意思，图像就如同纸币一样扎成一捆。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "批处理对计算机的运算大有利处，可以大幅缩短每张图像的处理时间。  \n",
    "那么为什么批处理可以缩短处理时间呢？  \n",
    "这是因为大多数处理数值计算的库都进行了能够高效处理大型数组运算的最优化。  \n",
    "并且，在神经网络的运算中，当数据传送成为瓶颈时，批处理可以减轻数据总线的负荷（严格地讲，相对于数据读入，可以将更多的时间用在计算上）。  \n",
    "也就是说，批处理一次性计算大型数组要比分开逐步计算各个小型数组速度更快。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "基于批处理的代码实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy:0.9352\n"
     ]
    }
   ],
   "source": [
    "x, t = get_data()\n",
    "network = init_network()\n",
    "batch_size = 100 # 批数量\n",
    "accuracy_cnt = 0\n",
    "\n",
    "for i in range(0, len(x), batch_size):\n",
    "    x_batch = x[i:i+batch_size]\n",
    "    y_batch = predict(network, x_batch)\n",
    "    p = np.argmax(y_batch, axis=1)\n",
    "    accuracy_cnt += np.sum(p == t[i:i+batch_size])\n",
    "print(\"Accuracy:\" + str(float(accuracy_cnt) / len(x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先是 range()函数。 range()函数若指定为range(start, end)，则会生成一个由start到end-1之间的整数构成的列表。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 range()函数生成的列表的基础上，通过x[i:i+batch_size]从输入数据中抽出批数据。  \n",
    "x[i:i+batch_n]会取出从第i个到第i+batch_n个之间的数据。  \n",
    "本例中是像x[0:100]、 x[100:200]……这样，从头开始以100为单位将数据提取为批数据。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，通过argmax()获取值最大的元素的索引。  \n",
    "不过这里需要注意的是，我们给定了参数axis=1。  \n",
    "这指定了在100 × 10的数组中，沿着第1维方向（以第1维为轴）找到值最大的元素的索引（第0维对应第1个维度）。  \n",
    "这里也来看一个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 1, 0])"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.array([[0.1, 0.8, 0.1], [0.3, 0.1, 0.6],[0.2, 0.5, 0.3], [0.8, 0.1, 0.1]])\n",
    "y = np.argmax(x, axis=1)\n",
    "y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，我们比较一下以批为单位进行分类的结果和实际的答案。  \n",
    "为此，需要在NumPy数组之间使用比较运算符（==）生成由 True/False构成的布尔型数组，并计算True的个数。  \n",
    "我们通过下面的例子进行确认:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ True  True False  True]\n"
     ]
    }
   ],
   "source": [
    "y = np.array([1, 2, 1, 0])\n",
    "t = np.array([1, 2, 0, 0])\n",
    "print(y==t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(y==t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.7、小结"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "1、神经网络中的激活函数使用平滑变化的sigmoid函数或ReLU函数。\n",
    "2、通过巧妙地使用NumPy多维数组，可以高效地实现神经网络。\n",
    "3、机器学习的问题大体上可以分为回归问题和分类问题。\n",
    "4、关于输出层的激活函数，回归问题中一般用恒等函数，分类问题中一般用softmax函数。\n",
    "5、分类问题中，输出层的神经元的数量设置为要分类的类别数。\n",
    "6、输入数据的集合称为批。通过以批为单位进行推理处理，能够实现高速的运算\n",
    "'''"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
